#!/bin/sh
# -* coding: UTF-8 *-

# Common functions
#EBUILD="yes"
[[ ${RC_GOT_FUNCTIONS} != "yes" ]] && source /sbin/functions.sh

# void efinish(int error, char* errstr)
#
#    indicate the completion of process
#    if error, show errstr via eerror and exit
#
efinish() {
    local retval="${1:-0}"
    shift

    eend "${retval}" "$@"
    if [ ${retval} -ne 0 ] ; then
        exit ${retval}
    fi
    return 0
}

# void load_project_config(char *args[])
#
# 	...
#
load_project_config() {
    luf_config=$1 ; shift
    if [ -z "${luf_config}" ] ; then
        eerror "no project specified"
        exit 1
    fi

    local retval
    ebegin "Reading project configuration"
    luf_config=$(get_absolute_path "${luf_config}") ; retval=$?
    if [ ${retval} -eq 0 ] ; then
        if [ -e "${luf_config}" ] ; then
            source "${luf_config}"
        else
            retval=1
        fi
    fi
    efinish ${retval}

    if [ -n "$1" ] ; then
        local device=$1 ; shift
        if [ -b "${device}" ] ; then
            luf_device=${device}
        else
            ewarn "${device} not valid block device"
        fi
    fi

    if [ -n "${luf_device}" ] ; then
        einfo "Using ${luf_device} device"
    fi
    luf_config_dir=$(dirname ${luf_config})
}

# bool _is_mount_something(void)
#
#       ...
#       for internal use
#
_is_mount_something() {
    local device
    local mountpoint
    local fstype
    local options
    local dump
    local order

    while read device mountpoint fstype options dump order ; do
        if [[ "${mountpoint}" == *"${luf_workdir}/source"* ]] ; then
            # echo "${mountpoint}"
            return 0
        fi
    done < /proc/mounts
    return 1
}

# char *get_absolute_path(char *pathname)
#
#	...
#
get_absolute_path() {
    local pathname=$1; shift
    local olddir


    # if no pathname specified, treat as current
    # working directory
    if [[ -z "${pathname}" ]] ; then
        pwd
        return 0
    fi

    # if pathname is a directory, change working directory
    # to it and print PWD
    if [[ -d "${pathname}" ]] ; then
        olddir=$(pwd)
        cd "${pathname}"
        pwd
        cd "${olddir}"
        return 0
    fi

    # if it is a filename or the pathname point to not existing
    # file try recursive make absolute pathname
    local retval
    local basename=$(basename "${pathname}")
    local dirname=$(dirname "${pathname}")

    # if dirname existst in the filesystem, and it not is directory
    # we can't determine the absolute pathname for it
    if [[ -e "${dirname}" ]] ; then
        if [[  -d "${dirname}" ]] ; then
            olddir=$(pwd)
            cd "${dirname}"

            dirname=$(pwd)
            [[ "${dirname}" = "/" ]] && dirname=""
            echo "${dirname}/${basename}"
            cd "${olddir}"
            return 0
        fi
    fi
    return 1
}

# char *rsync_path
#
#       ...
#
rsync_path="$(which rsync 2> /dev/null)"

# char *squashfs_path
#
#       ...
#
squashfs_path=$(which mksquashfs 2> /dev/null)

# char *sfdisk_path
#
#       ...
#
sfdisk_path=$(which sfdisk 2> /dev/null)

# char *mke2fs_path
#
#       ...
#
mke2fs_path=$(which mke2fs 2> /dev/null)

# char *grub_install_path
#
#	...
#
grub_install_path=$(which grub-install 2> /dev/null)

# void do_rsync(...)
#
#	check and call rsync
#
do_rsync() {
    if [ -x "${rsync_path}" ] ; then
        local retval

        ${rsync_path} "$@" 2> /dev/null ; retval=$?
        if [ ${retval} -ne 0 ] ; then
            eerror "rsync command failed"
        fi
        return ${retval}
    fi
    eerror "rsync command not found"
    return 1
}

# void luf_depends()
#
#       ...
#
luf_depend() {
    local retval

    if [ -z "${luf_workdir}" ] ; then
        eerror "working directory path not set"
        return 2
    fi

    if [ -z "${luf_kernel_archive}" ] ; then
        eerror "default kernel archive not set"
        return 10
    fi

    if [ ! -e "${luf_kernel_archive}" ] ; then
        eerror "default kernel archive does not exists"
        return 11
    fi

    if [ ! -x /usr/bin/tar ] ; then
        eerror "/usr/bin/tar command not found"
        return 8
    fi

    if [ -z "${sfdisk_path}" -o ! -x "${sfdisk_path}" ] ; then
        eerror "sfdisk program not found"
        return 8
    fi

    if [ -z "${mke2fs_path}" -o ! -x "${mke2fs_path}" ] ; then
        eerror "mke2fs program not found"
        return 8
    fi

    if [ -z "${grub_install_path}" -o ! -x "${grub_install_path}" ] ; then
        eerror "grub-install program not found"
        return 8
    fi

    if _is_mount_something ; then
        eerror "one or more filesystem still mounted inside ${luf_workdir}/source"
        eerror " please unmount it end try copy again"
        return 6
    fi
    return 0
}

# void do_in_chroot(char *function_name,...)
#
#	...
#
do_in_chroot() {
    local retval
    local function_name=$1 ; shift

    if [ ! -d "${luf_workdir}"/source ] ; then
        eerror "${luf_workdir}/source does not exist"
        return 3
    fi

    if [ -e "${luf_workdir}"/source/var/state/.splitted ] ; then
        eerror "stage was splitted, please clean workdir end try copy again"
        return 6
    fi

    # If user init_workdir function specified run it
    if [[ $(type -t "${function_name}") == "function" ]] ; then
        echo -e "#!/bin/sh\n" > "${luf_workdir}"/source/root/.do_in_chroot.sh
        chmod 0700 "${luf_workdir}"/source/root/.do_in_chroot.sh

        echo -e "[[ \${RC_GOT_FUNCTIONS} != \"yes\" ]] && source /sbin/functions.sh\n" \
            >> "${luf_workdir}"/source/root/.do_in_chroot.sh

        echo "luf_kernel_name=\"${luf_kernel_name}\"" \
            >> "${luf_workdir}"/source/root/.do_in_chroot.sh

        declare -f "${function_name}" \
            >> "${luf_workdir}"/source/root/.do_in_chroot.sh

        echo -e "\n${function_name} \"\$@\" ; retval=\$?\nexit \${retval}" \
            >> "${luf_workdir}"/source/root/.do_in_chroot.sh

        #devpts /dev/pts devpts rw,nosuid,noexec 0 0
        mount -t proc proc "${luf_workdir}"/source/proc
        chroot "${luf_workdir}"/source /root/.do_in_chroot.sh "$@" ; retval=$?
        umount "${luf_workdir}"/source/proc

        rm -f "${luf_workdir}"/source/root/.do_in_chroot.sh
        > "${luf_workdir}"/source/etc/mtab
        return ${retval}
    fi
    return 0
}

# void luf_init_workdir
#
#	...
#
luf_init_workdir() {
    local retval

    if [ ! -d "${luf_workdir}" ] ; then
        ewarn "${luf_workdir} does not exist"
        mkdir -p "${luf_workdir}" ; retval=$?

        if [ ${retval} -ne 0 ] ; then
            eerror "can't create ${luf_workdir}"
            return 3
        fi
    fi

    if [ "${luf_workdir_clean}" = "yes" ] ; then
        if _is_mount_something ; then
            eerror "one or more filesystem still mounted inside ${luf_workdir}/source"
            eerror " please unmount it end try copy again"
            return 6
        fi

        ebegin "clean working directory"
        rm -rf "${luf_workdir}"/*
        eend $?
    fi

    mkdir -p "${luf_workdir}"/source
    mkdir -p "${luf_workdir}"/target

    return ${retval}
}

# void luf_copy_stage
#
#	...
#
luf_copy_stage() {
    local retval

    if [ -z "${luf_workdir}" ] ; then
        eerror "working directory path not set"
        return 2
    fi

    if [ ! -d "${luf_workdir}"/source ] ; then
        eerror "${luf_workdir}/source does not exist"
        return 3
    fi

    if [ -z "${luf_stagedir}" ] ; then
        eerror "stage directory path not set"
        return 4
    fi

    if [ ! -d "${luf_stagedir}" ] ; then
        eerror "${luf_stagedir} does not exist"
        return 5
    fi

    if [ -e "${luf_workdir}"/source/var/state/.splitted ] ; then
        eerror "stage was splitted, please clean workdir end try copy again"
        return 6
    fi

    if _is_mount_something  ; then
        eerror "one or more filesystem still mounted inside ${luf_workdir}/source"
        eerror " please unmount it end try copy again"
        return 6
    fi

    # copy stage
    ebegin "Copying files"
    do_rsync --delete --partial --archive "${luf_stagedir}"/* "${luf_workdir}"/source ; retval=$?
    eend ${retval}

    if [[ ${retval} -eq 0 ]] ; then
        # Remove unused files
        rm -f "${luf_workdir}"/source/etc/mtab
        rm -f "${luf_workdir}"/source/var/run/*.pid
        rm -f "${luf_workdir}"/source/var/state/*

        # Copy kernel
        ebegin "Unpack default kernel ${luf_kernel_name}"
        /usr/bin/tar -C "${luf_workdir}"/source -xjf ${luf_kernel_archive} ; retval=$?
        eend ${retval}
    fi

    return ${retval}
}

# char *luf_images[]
#
#       ...
#
luf_images="source"

# void create_image(char *image_name, char *path [, char *mount_options])
#
#       ...
#
create_image() {
    local image_name=$1 ; shift
    local path=$1 ; shift
    local mount_options=$1 ; shift

    [[ -z "${mount_options}" ]] && mount_options="noauto"
    [[ -z "${path}" ]] && path="/mnt/cdrom/"

    if [[ "${luf_images}" != *"source.${image_name}"* ]] ; then
        luf_images="${luf_images} source.${image_name}"

        mkdir -p "${luf_workdir}"/source/mnt/."${image_name}"
        (
            echo "# Create mountpoint for ${image_name} part"
            echo "mkdir -p /newroot/mnt/.\"${image_name}\""
        ) >> "${luf_workdir}"/target/cdupdate.sh

        echo -e "${path}image.${image_name}\t/mnt/.${image_name}\tsquashfs\t${mount_options},users,exec,suid,loop\t0\t0" \
            >> "${luf_workdir}"/source/etc/fstab
    fi

    mkdir -p "${luf_workdir}"/source."${image_name}"
}

# void move_to_image(char *image_name, char *file_to_move [, char *mount_options])
#
#       ...
#
move_to_image() {
    local image_name=$1 ; shift
    local file_to_move=$1 ; shift
    local mount_options=$1 ; shift

    [[ -z "${mount_options}" ]] && mount_options="noauto"

    if [[ "${luf_images}" != *"source.${image_name}"* ]] ; then
        luf_images="${luf_images} source.${image_name}"

        mkdir -p "${luf_workdir}"/source/mnt/."${image_name}"
        (
            echo "# Create mountpoint for ${image_name} part"
            echo "mkdir -p /newroot/mnt/.\"${image_name}\""
        ) >> "${luf_workdir}"/target/cdupdate.sh

        echo -e "/mnt/cdrom/image.${image_name}\t/mnt/.${image_name}\tsquashfs\t${mount_options},users,exec,suid,loop\t0\t0" \
            >> "${luf_workdir}"/source/etc/fstab
    fi

    mkdir -p "${luf_workdir}"/source."${image_name}"
    touch "${luf_workdir}"/source/var/state/.splitted

    if [ -z "${file_to_move}" -o -z "${image_name}" ] ; then
        ewarn "no file to move or no image"
        return 1
    fi

    local source_dir="${luf_workdir}"/source
    local target_dir="${luf_workdir}"/source."${image_name}"
    local dirname=$(dirname ${file_to_move})

    if [ -e "${source_dir}${file_to_move}" ] ; then
        eindent 1
        ebegin "Move ${file_to_move}"
        if [ -n "${dirname}" -a "${dirname}" != "/" ] ; then
            mkdir -p "${target_dir}${dirname}"
            mv "${source_dir}${file_to_move}" "${target_dir}${dirname}"/ > /dev/null 2>&1
            ln -fs /mnt/."${image_name}${file_to_move}" "${source_dir}${dirname}" > /dev/null 2>&1
        else
            mv "${source_dir}${file_to_move}" "${target_dir}"/ > /dev/null 2>&1
            ln -fs /mnt/."${image_name}${file_to_move}" "${source_dir}${file_to_move}" > /dev/null 2>&1
        fi
        eend $?
        eoutdent 1
    fi
    return 0
}

# void luf_build_squash_images()
#
#       ...
#
luf_build_squash_images() {
    local image
    local retval=0
    local squash_image

    > "${luf_workdir}"/mksquashfs.log
    for image in ${luf_images} ; do
        eindent 1
        ebegin "Pack ${image} squashfs image"

        echo "#### ${image}" >> "${luf_workdir}"/mksquashfs.log

        squash_image="image${image##source}"
        rm -f "${luf_workdir}"/target/"${squash_image}"
        ${squashfs_path} "${luf_workdir}"/"${image}" "${luf_workdir}"/target/"${squash_image}" >> "${luf_workdir}"/mksquashfs.log 2>&1 ; retval=$?
        chmod 0644 "${luf_workdir}"/target/"${squash_image}"

        eend ${retval}
        eoutdent 1
    done
    return ${retval}
}

# void make_out_flash()
#
#       ...
#
make_out_flash() {
    local retval

    ebegin "Clear partition table"
    dd if=/dev/zero of="${luf_device}" bs=512 count=2 > /dev/null 2>&1 ; retval=$?
    eend ${retval}
    [[ ${retval} -ne 0 ]] && return ${retval}

    if [ "${luf_data_use}" = "yes" ] ; then
        ebegin "Create partition (${luf_boot_label},${luf_data_label})"
        #echo -e "0,${luf_boot_size},L,*;\n,,L,-" | ${sfdisk_path} -q -uM "${luf_device}" > /dev/null 2>&1 ; retval=$?
        set -x
        echo -e "0,${luf_boot_size},L,*;\n,,L,-" | ${sfdisk_path} -q -uM "${luf_device}"  2>&1 ; retval=$?
        set +x
        eend ${retval}
        [[ ${retval} -ne 0 ]] && return ${retval}

        sleep 2
        ebegin "Make filesystem on boot partition (${luf_boot_label})"
        ${mke2fs_path} -q -L "${luf_boot_label}" -m0 "${luf_device}"1  ; retval=$?
        eend ${retval}
        [[ ${retval} -ne 0 ]] && return ${retval}

        ebegin "Make filesystem on data partition (${luf_data_label})"
        ${mke2fs_path} -q -L "${luf_data_label}" -m0 "${luf_device}"2  ; retval=$?
        eend ${retval}
        [[ ${retval} -ne 0 ]] && return ${retval}
    else
        ebegin "Create partition (${luf_boot_label})"
        echo -e "0,,L,*" | ${sfdisk_path} -q "${luf_device}" > /dev/null 2>&1 ; retval=$?
        if [[ ${retval} -eq 0 ]] ; then
            if [[ -n "${luf_boot_size}"  ]] ; then
                if [[ ${luf_boot_size} -gt 0 ]] ; then
                    echo -e "0,${luf_boot_size},L,*;\n,,L,-" | ${sfdisk_path} -q -N1 -uM "${luf_device}" > /dev/null 2>&1 ; retval=$?
                fi
            fi
        fi
        eend ${retval}
        [[ ${retval} -ne 0 ]] && return ${retval}

        sleep 2
        ebegin "Make filesystem on boot partition (${luf_boot_label})"
        ${mke2fs_path} -q -L "${luf_boot_label}" -m0 "${luf_device}"1  ; retval=$?
        eend ${retval}
        [[ ${retval} -ne 0 ]] && return ${retval}
    fi
    return ${retval}
}

# void write_to_usbflash()
#
#       ...
#
copy_to_usbflash() {
    local retval

    mkdir -p "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    mount "${luf_device}"1 "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    do_rsync --delete --partial --archive "${luf_workdir}"/target/* "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        umount -fl "${luf_workdir}"/mountpoint
        return ${retval}
    fi

    umount "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    rm -fr "${luf_workdir}"/mountpoint ; retval=$?
    eend ${retval}
    return ${retval}
}


# void install_grub()
#
#	...
#
install_grub() {
    local retval

    mkdir -p "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    mount "${luf_device}"1 "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    ${grub_install_path} --recheck --root-directory="${luf_workdir}"/mountpoint ${luf_device} > /dev/null 2>&1 ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        umount -fl "${luf_workdir}"/mountpoint
        return ${retval}
    fi

    umount "${luf_workdir}"/mountpoint ; retval=$?
    if [ ${retval} -ne 0 ] ; then
        return ${retval}
    fi

    rm -fr "${luf_workdir}"/mountpoint ; retval=$?
    eend ${retval}
    return ${retval}
}

# bool is_mount(char *path)
#
#       ...
#       for internal use
#
is_mount() {
    local device
    local mountpoint
    local fstype
    local options
    local dump
    local order
    local path=$1 ; shift

    [ -z "${path}" ] && return 1

    while read device mountpoint fstype options dump order ; do
        if [[ "${mountpoint}" == "${path}" ]] ; then
            # echo "${mountpoint}"
            return 0
        fi
    done < /proc/mounts
    return 1
}
